package com.lanxin.Cache;

import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class RedisMybatisCache implements Cache {

    //在MyBatis的缓存实现类中，构造方法需要的id参数是由MyBatis框架自身在创建缓存实例时自动传入的。
    // 这个机制是MyBatis缓存体系的核心设计之一。
    //这个id就是Mapper接口的全限定名（包括包名）
    //例如：com.example.mapper.UserMapper
    //主要是用来确保不同Mapper的缓存数据不会冲突
    private final String id;

    //所有mapper共用一块缓存区，所以需要使用静态变量来确保获取的RedisTemplate获取的是同一个对象，
    private static RedisTemplate<String, Object> redisTemplate;

    //构造方法，MyBatis在解析Mapper文件时会自动传入id
    public RedisMybatisCache(String id) {
        this.id = id;
    }

    //传入RedisTemplate实例，由于Cache实现类不受Spring容器管理，所以需要另写一个配置类来注入
    public static void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        RedisMybatisCache.redisTemplate = redisTemplate;
    }

    @Override
    public String getId() {
        return "";
    }

    //向Redis中存入数据，设置过期时间为1-120分钟内的随机正整数数，必须保证为正整数，否则会报错，防止内存雪崩。
    //key由MyBatis框架自动传入，组成通常是：Mapper 的 namespace（如 com.example.UserMapper）
    //SQL 语句的 ID（如 selectById）/执行的 SQL 语句本身/查询参数值（包括分页参数）/环境 ID（多数据源时区分环境）
    //value是查询的结果：可以是单个实体对象，集合对象，包装对象等
    @Override
    public void putObject(Object key, Object value) {
        redisTemplate.opsForValue().set(key.toString(), value, ThreadLocalRandom.current().nextInt(1, 121),
                TimeUnit.MINUTES);
    }

    @Override
    public Object getObject(Object key) {
        return redisTemplate.opsForValue().get(key.toString());
    }

    @Override
    public Object removeObject(Object key) {
        return redisTemplate.delete(key.toString());
    }

    @Override
    //由于template中没有封装清除操作，只能通过connection来执行
    public void clear() {
        redisTemplate.execute((RedisCallback<Void>) connection ->{
            connection.flushDb();
            return null;
        });
    }

    @Override
    public int getSize() {
        return redisTemplate.execute(RedisServerCommands::dbSize).intValue();
    }
}
